package com.it.myh.location.privacy.protection;

import com.it.myh.geographically.dispersed.GeoDis;
import com.it.myh.query.probability.TimeZone;
import com.it.myh.query.probability.queryProbability;
import com.it.myh.read.LocationAndTime;
import com.it.myh.semantic.BaiduMapReverseGeocoding;
import com.it.myh.semantic.JaroWinklerSimilarity;

import java.awt.geom.Point2D;
import java.time.LocalTime;
import java.util.*;
import java.util.stream.Collectors;

public class LocationPrivacyProtection {
    private static final String PROCESSED_DATA_SET_PATH = "C:\\Users\\IoV321\\Desktop\\result.txt";

    public static void main(String[] args) {
        LocationAndTime realLocation = new LocationAndTime(39.92622, 116.337558, LocalTime.parse("13:39:13"));
        List<LocationAndTime> dummyLocationGeneration = getDummyLocationGeneration(realLocation, 4, 0.2);
        for (LocationAndTime locationAndTime : dummyLocationGeneration) {
            System.out.println(locationAndTime);
        }
    }
    public static List<LocationAndTime> getDummyLocationGeneration(LocationAndTime real, int k, double l){
        TimeZone realTimeZone = TimeZone.whichZone(real.getTime());
        List<LocationAndTime> locationAndTimes = queryProbability.deserializeLocationAndTime(PROCESSED_DATA_SET_PATH);
        List<LocationAndTime> allLocationsByTimeZone = getLocationsByTimeZone(realTimeZone, locationAndTimes);
        List<LocationAndTime> get4Locations = get4kLocationsByProp(allLocationsByTimeZone, real, k);
        List<LocationAndTime> get2Locations = get2kLocationsBySemantic(get4Locations, real, k, l);
        List<LocationAndTime> location = getkLocationsByGeoDis(get2Locations, k);
        return location;
    }
    //获取某个时区下的所有位置
    public static List<LocationAndTime> getLocationsByTimeZone(TimeZone realTimeZone, List<LocationAndTime> locationAndTimes){
        List<LocationAndTime> locationsByTimeZone = new ArrayList<>();
        for (LocationAndTime locationAndTime : locationAndTimes) {
            if (TimeZone.isSameTimeZone(realTimeZone,TimeZone.whichZone(locationAndTime.getTime()))){
                locationsByTimeZone.add(locationAndTime);
            }
        }
        return locationsByTimeZone;
    }


    //根据查询概率选取相近的4k个假位置
    public static List<LocationAndTime> get4kLocationsByProp(List<LocationAndTime> locations, LocationAndTime real, int k){
        int i = 0;
        List<LocationAndTime> get8kLocationsByProp = new ArrayList<>();
        List<LocationAndTime> allLocationSorted = queryProbability.sortPro(locations);
        LocationAndTime realLocation = LocationAndTime.returnSameLocation(real, allLocationSorted);

        System.out.println("真实位置：" + realLocation + BaiduMapReverseGeocoding.getSemantic(realLocation));

        Double probability = realLocation.getProbability();
        int index = allLocationSorted.indexOf(realLocation);
        allLocationSorted.remove(index);
        if ((index + 1) < (4 * k)){
            while (i < (8 * k)){
                get8kLocationsByProp.add(allLocationSorted.get(i));
                i++;
            }
        } else if ((allLocationSorted.size() - index) < (4 * k)){
            while (i < (8 * k)){
                get8kLocationsByProp.add(allLocationSorted.get(allLocationSorted.size() - (i + 1)));
                i++;
            }
        } else {
            while (get8kLocationsByProp.size() != (8 * k)){
                get8kLocationsByProp.add(allLocationSorted.get((index - (4 * k)) + i));
                i++;
            }
        }
        Map<LocationAndTime,Double> map =new HashMap<>();
        for (int j = 0; j < get8kLocationsByProp.size(); j++) {
            map.put(get8kLocationsByProp.get(j),Math.abs(probability - get8kLocationsByProp.get(j).getProbability()));
        }
        Comparator<Map.Entry<LocationAndTime,Double>> valueComparator = Map.Entry.comparingByValue();
        List<Map.Entry<LocationAndTime, Double>> sortedEntries = map.entrySet().stream()
                .sorted(valueComparator)
                .collect(Collectors.toList());
        List<LocationAndTime> get4kLocationsByProp = new ArrayList<>();
        for (Map.Entry<LocationAndTime, Double> sortedEntry : sortedEntries) {
            if (get4kLocationsByProp.size() != (4 * k)){
                get4kLocationsByProp.add(sortedEntry.getKey());
            }
        }
        return get4kLocationsByProp;
    }

    public static List<LocationAndTime> get2kLocationsBySemantic(List<LocationAndTime> locations, LocationAndTime real, int k, double l){
        List<LocationAndTime> get2kLocations = new ArrayList<>();
        String realSemantic = BaiduMapReverseGeocoding.getSemantic(real);
        //初步构建2k
        for (LocationAndTime location : locations) {
            String semantic = BaiduMapReverseGeocoding.getSemantic(location);
            double dif = JaroWinklerSimilarity.semanticDif(realSemantic, semantic);
            if (dif >= l && get2kLocations.size() != (2 * k)){
                get2kLocations.add(location);
            }
        }
        if (get2kLocations.size() >= 2){
            //再次构建2k
            for (int i = 0; i < get2kLocations.size(); i++) {
                String semantic1 = BaiduMapReverseGeocoding.getSemantic(get2kLocations.get(i));
                for (int j = i + 1; j < get2kLocations.size(); j++) {
                    String semantic2 = BaiduMapReverseGeocoding.getSemantic(get2kLocations.get(j));
                    if (JaroWinklerSimilarity.semanticDif(semantic1,semantic2) < l){
                        get2kLocations.remove(j);
                    }
                }
            }
        }
        return get2kLocations;
    }
    public static List<LocationAndTime> getkLocationsByGeoDis(List<LocationAndTime> locations, int k){
        if ((locations.size() + 1) <= k){
            return locations;
        } else {
            List<LocationAndTime> getkLocations = new ArrayList<>();
            List<Point2D.Double> arr = new ArrayList();
            for (LocationAndTime location : locations) {
                arr.add(new Point2D.Double(location.getLatitude(),location.getLongitude()));
            }
            List<Point2D.Double> allLocationHull = GeoDis.calculateConvexHull(arr);
            List<Point2D.Double> selectedFakeLocations = GeoDis.getLargestConvexHull(allLocationHull, k);
            System.out.println("凸包面积为：" + GeoDis.calculateConvexHullArea(selectedFakeLocations));
            for (int i = 0; i < selectedFakeLocations.size(); i++) {
                for (int j = 0; j < locations.size(); j++) {
                    if (selectedFakeLocations.get(i).getX() == locations.get(j).getLatitude() && selectedFakeLocations.get(i).getY() == locations.get(j).getLongitude()){
                        getkLocations.add(locations.get(j));
                    }
                }
            }
            return getkLocations;
        }
    }

}
